Ad Hoc(一)

简介

本文仅显示部分代码,完整代码参见The one
关于the one 的学习,首先来解读一下官方提供的Readme.txt文件。
官方提供的readme.txt文件中的描述为:ONE是一个机会网络环境模拟器,它提供了一个功能强大的工具,可用于生成移动性跟踪,使用不同的路由协议运行DTN消息传递模拟,以及实时交互式地可视化这两个模拟以及完成后的结果。

这里的DTN表示时延容忍网络(Delay Tolerant networks)在一些特定的环境中,会经常出现网络断开的现象,导致报文在传输过程中不能确保端到端的路径,这类网络被称为时延容忍网络。

所有仿真参数都使用配置文件给出。这些文件是包含键值对的普通文本文件。 大多数变量的语法为:Namespace.key = value。

配置文件

  • default_settings.txt
    文件“ default_settings.txt”(如果存在)始终被读取,而另一个
    作为参数给出的配置文件可以定义更多设置,也可以覆盖先前文件中的某些(甚至所有)设置。

    运动模型控制节点

    主要是movement下的内容:
    它们为节点提供坐标,速度和暂停时间。基本的模式有:
  • RandomWaypoint.java
    随机航点运动模型,在模拟区域内创建锯齿形路径;当节点使用随机航点运动模型(RandomWaypoint)时,会在模拟区域中为其指定随机坐标。 节点以恒定的速度直接移动到给定的目的地,暂停片刻,然后获取新的目的地。 在整个模拟过程中,这种情况一直持续,并且节点沿这些之字形路径移动.
  • mapBasedMovement.java
    基于地图的移动,该模型给出使用SimMap道路的路径;基于地图的运动模型将节点的运动限制为预定义的路径。可以定义不同类型的路径,并且可以为所有节点组定义有效路径。 这样,可以防止例如汽车在室内或人行道上行驶。(MapBasedMovement)首先在两个相邻(即通过路径连接)的地图节点之间分配节点,然后节点开始从相邻的地图节点移动到另一个地图节点。 当节点到达下一个地图节点时,它会随机选择下一个相邻的地图节点,但只有在唯一的选择时才选择它来自的地图节点(即避免返回它的来源)。 节点通过10-100个地图节点移动后,会暂停一会儿,然后再次开始移动。
  • ShortestPathMapBasedMovement.java
    基于地图的最短路径的移动,该模型使用Dijkstra算法查找两个随机地图节点与兴趣点之间的最短的路径。基本的基于地图的移动模型;使用Dijkstra最短的pat算法来查找其穿过地图区域的方式。 一旦节点到达其目的地并等待了暂停时间,便会选择一个新的随机地图节点,并使用最短路径(仅可使用有效地图节点才能采用的最短路径)将节点移动到那里。
    对于基于最短路径的运动模型,地图数据还可以包含兴趣点(POI)。 代替为下一个目的地选择任何随机地图节点,可以将运动模型配置为以可配置的概率给出属于某个POI组的POI。 可以有无限数量的POI组,并且所有组可以包含任意数量的POI。 对于所有POI组,所有节点组都可以具有不同的概率。 POI可用于建模例如商店,饭店和旅游景点。
  • MapRouteMovement.java
    地图路线移动,使用在一个区域内预定的地图路径。使用这个模型的节点可以在每个录像航点上停下来。使用最短路径算法查找到下一个航点的路线。可以有不同类型的路线。基于路线的运动模型(MapRouteMovement)可用于为遵循某些路线的节点建模,例如 巴士或电车线。 只需定义路径上的站点,然后使用该路径的节点将使用最短路径从站点移到站点,然后在配置的时间内在站点上停止。
    所有运动模型还可以确定节点何时处于活动状态(运动并且可以连接至该节点),以及何时不处于活动状态。 对于所有模型,除了外部运动之外,都可以指定多个模拟时间间隔,并且该组中的节点仅在这些时间段内处于活动状态。
  • ExternalMovement.java
    使用外部运动数据(ExternalMovement)的实验运动模型从文件中读取带有时间戳的节点位置,并相应地在仿真中移动节点。 有关格式的详细信息,请参见输入包中的ExternalMovementReader类的javadocs。 工具箱文件夹中包含适用于TRANSIMS数据的实验性转换条(transimsParser.pl)。
  • movementModel
    使用“ movementModel”设置为每个节点组定义要使用的运动模型。 设置的值必须是来自运动包的有效运动模型类名称。 所有运动模型共有的设置在MovementModel类中读取,运动模型特定的设置在相应类中读取。 有关详细信息,请参见javadoc文档中的示例配置文件。

    消息路由

    • MessageRouter
      消息路由器的超类,这个类规定了一些通用的设置
    • 消息缓冲区的设置 bufferSize
    • 消息的TTL设置 msgTtl
    • 消息的队列模型设置 sendQueue (主要提供了两个模型,参数值设置是通过设置数字来表示的 )
      • 1 RANDOM 设置,消息的顺序在每一个时间点是随机的
      • 2 FIFO 设置 ,最后收到的消息最后发送
        • 当被请求开始传输数据的时候的返回值
        • RCV_OK(0)表示主机开始接收消息,并且开始传输消息,小于0 表示现在不接收这类特定消息 大于0 表示主机现在不能接收任何消息
        • TRY_LATER_BUSY 1 接收器繁忙的返回值
        • DENIED_OLD -1 接收到已经接收过的消息的返回值
        • DENIED_NO_SPACE -2 接收返回值,因为缓冲区中没有足够的空间用于消息
        • DENIED_TTL = -3 接收TTL已过期的消息的返回值
        • DENIED_LOW_RESOURCES = -4 接收节点的返回值(某些资源不足)
        • DENIED_UNSPECIFIED = -99 接收返回值,原因不明
        • MAX_TTL_VALUE = 35791394 最大的TTL值
          消息缓冲队列分为 message buffer 以及 incoming messages buffer
    • ActiveRouter
      Active routers的超类(正在活动-使用的路由器的超类?)包含传输方式以及监听发送链接
  • EpidemicRouter
    泛洪消息路由器,丢弃最老的消息,一次只能传输单个消息
  • 相关工具类以及接口

  • /core/MessageListener 消息监听器
    用于监听主机之间消息的传输
  • 数据格式的输入

    所有基于地图的模型都使用以“Well Known Text”(WKT)格式的子集格式化的文件来获取输入数据。
    • 解析器支持WKT文件的LINESTRING和MULTILINESTRIN伪指令,用于映射路径数据。(MULTI)LINESTRING中的相邻节点被视为形成路径,如果某些线包含一些具有完全相同坐标的顶点,则路径将从这些位置连接起来(这是创建相交的方式)。
    • 对于点数据(例如用于POI),还支持POINT指令。
    • WKT files can be edited and generated from real world map data using any suitable Geographic Information System (GIS) program.
    • The map data included in the simulator distribution was converted and edited using the free, Java based OpenJUMP GIS program.
    • 不同地图类型的数据被存储在不同的文件中
    • 一个WKT文件可以包含多个路由,并且按照在文件中出现的顺序将它们分配给节点。
    • 实验过程

      现在很明显,我们要使用的是ExternalMovement.java这一个文件。
      下面来分析一下这一个文件的作用是什么。
      从这个java文件的结构上来看,这个文件属于一个实体类。
      可以初始化一个有着时间-位置元组的运动模型。

      文件的组成

      文件的第一行应该是offset 头,目前的z轴是可以忽略的,可以在文件中出现。
      minTime maxTime minX maxX minY maxY minZ maxZ

下面几行的语法为:
time id xPos yPos

所有行都必须按时间排序。 整个文件的采样间隔(两个时间实例之间的时间差)必须相同。

一些小的细节(一) tookit的使用

  • 当你想要将自己的数据喂给模拟器的时候,如果你需要将你整理好的数据格式 使用tookit中的pl工具进行转化。
    pl工具是用Perl脚本语言进行编写的,如果自己的电脑上没有安装perl工具,就 需要安装perl。运行perl -v 检查自己的工具是不是已经安装成功。
    接下来我在去运行 pl程序。
    我这里要用到的是transimsParser.pl 这个。在运行这个程序的时候出现了两个错误:
    • 第一个是 /usr/local/bin/perl: bad interpreter: No such file or directory
      经过对perl语言的了解,代码中 /usr/local/bin/perl 是 perl 解释器的路径。
      liunx下一般我们的解释路径有两种 格式 一种是 /usr/bin/perl,另一种是/usr/local/bin/perl。可以通过查看并修改脚本,尝试一下
    • 第二个是 Can’t locate Common.pm in @INC (you may need to install the Common module)
      因为这个程序不是我写的,从这个日志上我们可以看出,脚本是因为无法定位到Common.pm模块导致的。第一次接触perl脚本,我就查找了一些安装扩展库的一些方法,但是最终没有找到common这个模块,具体应该安装哪一个。
      对程序分析后,发现这个Common.pm这个是软件设计者自己写的。那么我就将这个模块所在的文件夹,全部用作库文件。在脚本中添加了一个
      use lib ‘/文件模块所在的文件夹‘;
      然后就解决了这个问题;下面是我的配置:
1
2
3
4
5
6
7
#! /usr/bin/perl
package Toolkit;
# Transims vehicle shapshot file parser
use strict;
use warnings;
use lib '/Users/gorge/Downloads/the-one-master/toolkit';
use Common; # parseArgs and debug methods

实验进度一

经过上面的调试,我们的程序使用测试数据运行已经是没有问题的了!
我们接下来要做的是已经知道船舶的运行轨迹数据,我们要做一个仿真,恢复一下船舶的真实运行情况!
我们所有的船舶的数据是存在在txt文件中的。下面我将详细的记录一下整个实验过程。
下面是我们已经处理过的一部分数据了。这是数据格式,我们要关注的是船泊的编号、我这里使用发送时间、已经船舶的经度纬度。其余的就不在关注 了。

1
2
[10032.0, 2.0, 24567613, '2016-09-17 04:13:07', 30.466565, 122.27363166666666, 0.0, 0.0, 0.0, 0.0]
[10032.0, 2.0, 24567643, '2016-09-17 05:13:07', 30.466556666666666, 122.27364333333334, 0.0, 0.0, 0.0014507762179330167, 0.0]

文件目录

1
2
3
4
5
6
7
8
9
10
//文件读取 获得文件夹的列表 并返回一个list(这个list只包含 文件夹中的名称)
public static String [] getFilePath(String filepath){
String res [] = null;
File file = new File(filepath);
//苹果产品 下注意.DS_Store隐藏文件的问题。win下同样也要注意隐藏文件
if(file.isDirectory()) {
res = file.list();
}
return res;
}

文件路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//进行文件路径的读取 与拼接这部分主要是排除我们不需要的数据文件 
/**
*
* @param path 这个参数是用来接收所有的读取文件的
* @param nouse 这个参数是用来增加所有的不使用的文件
*/
public static List<String> getpathList(String [] pa,List<String> nouse){
List<String> geList = new ArrayList<String>();
for(int i=0;i<pa.length;i++) {
if(!nouse.contains(pa[i])&&!pa[i].equals(".DS_Store")) {
geList.add("/Users/gorge/Desktop/newshipData/"+pa[i]);
}
}
return geList;
}

原始文件读取并返回List<List>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public List<List<String>> readFILE2(String path) {
File file = new File(path);
//
List<List<String>> allList = new ArrayList<List<String>>();
BufferedReader reader = null;
try {
Reader inputStream = new FileReader(file);
reader = new BufferedReader(inputStream);
String line = null;
while((line = reader.readLine())!=null) {
String liString [] = line.substring(1, line.lastIndexOf("]")).split(",");
List<String> list = new ArrayList<String>();
for(String s:liString) {
list.add(s);
}
allList.add(list);
}
return allList;

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}

时间转换

这部分是之前用python写的,对于这部分的理解,可以帮助我们对三分钟的记录进行处理,可以看到我们的dataOrgset的索引为2的位置,已经将原来的秒,转换成了分钟,并且进行了取整。

1
2
3
4
5
6
7
8
#定义一个数据集 用来转换输出格式 更直观的能看到数据
for j in range(len(dataOrgset)):
#timeArray1 = time.localtime(float(dataOrgset[j][2]))
#othertime = time.strftime("%Y-%m-%d %H:%M:%S",timeArray1)
dataOrgset[j][2]=int(float(dataOrgset[j][2])/60)#发送信号的时间 4
timeArray2 = time.localtime(float(dataOrgset[j][3]))
othertime2 = time.strftime("%Y-%m-%d %H:%M:%S",timeArray2)
dataOrgset[j][3]=othertime2#接收信号的时间 5

下面,我们要思考的是如何将时间设置成每条船每隔3分钟取一个记录。我现在的思考是 先从2016年8月31日的零点开始,获得一个秒的时间。然后转化成分钟,然后每隔3分钟进行累加一次,创建一个时间序列。

1
2
3
4
5
import time,datetime
match_time = '2016-08-31 00:00:00'
ans_time_stamp = time.mktime(time.strptime(match_time, "%Y-%m-%d %H:%M:%S"))
print (int(float(ans_time_stamp)/60));
#结果 24542880

我们就是基于上述结果进行每3分钟累加,创建一个时间序列,一直到10月1日的00点 的分钟数为 24586080
(a = (24587520-24542880)/3) = 14880.0 也就是说每条船至少要有14880.0个时间戳,可能位置为空(这一部分我们先处理好)
我这里打算使用map进行映射!筛选出我们每分钟的数据来

1
2
3
4
5
6
7
8
9
//每三分钟获得一次 从2016年8月31号 (这里初始化一个时间序列)
public Map<Integer,List<String>> initTime(){
//开始时间是24542880 结束时间是24587520
Map<Integer,List<String>> timeMap= new LinkedHashMap<Integer,List<String>>();
for(int i=24542880;i<=24587520;i+=3) {
timeMap.put(i, null);
}
return timeMap;
}

经过上述操作,对于每一条船,我们就有了一个全局时间戳的 map。下面我们就要将每条船的信息进行填充。
这部分的处理需要分两种情况:

  • 连续性空缺
    连续性空缺的意思是已经存在的船舶的数据的记录,两条记录的时间间隔相差较大,对于这部分的处理
  • 非连续性空缺
    有长时间的船舶数据的记录的缺失

这里先简单的测试一下结果,版本1.1这部分后期可能还会在优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//使用时间序列 将已经读取的数据进行 筛选
public Map<Integer,List<String>> setSingleShip(Map<Integer,List<String>> timeMap,List<List<String>> file1){
for(int i=0;i<file1.size();i++) {
String id = file1.get(i).get(0);
String curtime = file1.get(i).get(2);
String lon = file1.get(i).get(5);
String lat = file1.get(i).get(4);
String xyString [] = millerToXY(Double.parseDouble(lon), Double.parseDouble(lat));
/**下面就要处理一下时间的问题(下面是一个初步的设置 位置就选3分钟间隔内的任意一个)(因为map中的时间是按照时间序列排列的
文件中的时间也是按照时间序列排列的,我们就通过遍历文件序列,去找一个map的键)**/
//先找一个时间的差值
int Icurtime = Integer.parseInt(curtime);
int diftime = Icurtime- 24542880;
List<String> line = new LinkedList<String>();
if(diftime%3==0) {
if(timeMap.get(Icurtime).isEmpty()) {
line.add(id);
line.add(xyString[0]);
line.add(xyString[1]);
timeMap.replace(Icurtime, line);
}else {
continue;
}
}else {
int Tkey =Icurtime-diftime%3;
if(timeMap.get(Tkey).isEmpty()) {
line.add(id);
line.add(xyString[0]);
line.add(xyString[1]);
timeMap.replace(Tkey, line);
}else {
continue;
}
}
}
return timeMap;
}

部分代码重新更新一次(这样计算出来的速度是船泊正常速度的2-3倍)

这里的原因,应该是我在进行数据处理的时候,对于每三钟取一次数据,当目标不为空的时候,我直接跳过了,而没有用新的数据进行更新。所以可能存在大部分的轨迹数据,是

坐标转换

经纬度转笛卡尔坐标系 ,地球是一个球形,我们要将地图上的数据,在平面坐标系上运行,则必须进行这个过程。(这个过程中,因为转化完的数据过大,我做了一个数据的放缩,)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
*
* @param lon 经度
* @param lat 纬度
* @return 这里做一个经纬度转笛卡尔乘积的转换函数
*/
public static String [] millerToXY(double lon,double lat) {
String [] xy_coordinate = new String [2];
//地球周长
double L = 6381372*Math.PI*2;
//将平面展开,将周长视为X轴
double W = L;
//Y轴约等于周长一般
double H = L/2;
double mill =2.3; //米勒投影中的一个常数,范围大约在正负2.3之间
double x = lon*Math.PI/180; //将经度从度数转换为弧度
double y = lat*Math.PI/180; //将纬度从度数转换为弧度
y = 1.25*Math.log(Math.tan(0.25*Math.PI+0.4*y)); //这里是米勒投影的转换
//这里将弧度转为实际距离 ,转换结果的单位是公里
x = (W/2)+(W/(2*Math.PI))*x;
y = (H/2)-(H/(2*mill))*y;
xy_coordinate [0]= String.format("%.2f", (x-33700000)/10);
xy_coordinate[1] = String.format("%.2f", (y-7600000)/10);
return xy_coordinate;
}

文件输出

这里为了减少对文件的读写操作,选择将文本直接处理成一个字符串进行读写。减少了IO开销。这部分空缺值的处理 后期还会在修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//字符串的拼接
public static String WriteValue(Map<Integer,List<String>> file) {
String value = "";
Set<Integer> key = file.keySet();
for (Integer k : key) {
//如果key对应的值为空怎么办?
value = value+" "+k;
if(!file.get(k).isEmpty()) {
LinkedList<String> line = (LinkedList<String>) file.get(k);
for(int i =0;i<line.size();i++) {
value = value+" "+line.get(i);
}
value+="\n";
}else {
value+="\n";
}
}
return value;
}

写入字符流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/由列表写入文件 注意这里写入的是一个字符串
public static void writerFile(String path,String value) {
File file = new File(path);
if(!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("文件创建失败");
}
}
try {
FileOutputStream output = new FileOutputStream(file);
OutputStreamWriter osw = new OutputStreamWriter(output,"UTF-8");
osw.write(value);
osw.close();
output.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

文件转换主函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//通过传入文件夹的路径,将数据转化为可以使用的格式
public static void transDataFormat(String path,String targ) {
//首先 获取文件列表(传入文件路径)
String [] filedir= getFilePath(path);
//进行文件链路的拼接 这一个集合是排除的集合
List<String> nouse = new ArrayList<String>();
//String head = "";
List<String> filep = getpathList(path,filedir, nouse);
FileRead fRead = new FileRead();
for(int i=0;i<filep.size();i++) {
String aaa = filep.get(i);
List<List<String>> file1 = fRead.readFILE2(aaa);
Map<Integer, List<String>> file2 = setSingleShip(file1);
String value = WriteValue(file2);
writerFile(targ+aaa.substring(aaa.lastIndexOf("/")), value);
}
}

到此为止,我们已经拥有了一份格式相对完整的数据了。但是这份数据的内容还完整,数据值的缺失问题,我们该怎么解决?
有部分数据是这样的:

1
2
3
4
5
6
7
24575451 11197.0  30.759755  123.775865
24575454
24575457
24575460 11197.0 30.761711666666667 123.776965
24575463
24575466
24575469 11197.0 30.759831666666667 123.77584333333333

还有一部分数据是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
24574605 11197.0  30.6768  123.77771
24574608
24574611
24574614
24574617
24574620
24574623
24574626
24574629
24574632
24574635 11197.0 30.6768 123.77793166666666
24574638

还有一部分数据是这样的:
这部分与上面最大的区别就是连续好几天的位置记录是空缺的。对于这部分数据我们该怎么弄?

1
2
3
4
5
6
7
24569856
24569859
24569862
24569865
24569868
24569871
.....

筛选数据

上面出现的很多空却的位置数据,有很多可能是对我们实验并没有什么帮助的,因此我们要对数据进行筛选,筛选出这些数据中,有船的数据比较多的时间,以及船。

  • 初始化一个空的时间键值对,时间序列作为键,船的编号作为值
  • 依次迭代每个文件,然后更新值
    • 遍历第一个文件的时候,初始化Map的值,判断该时刻下是否有船的值.这个过程我们可以通过初始化两个map来实现,这两个map的键是同步的,第一个map相当于一个文件,第二个map用来存储这次值的筛选。
    • 每读取一个文件会构造出第一个map,然后根据第一个map更新第二个map。
    • 更新过程可以设置为根据第二个map的键作为第一个map的键,去查找第一个map中,该键下是否存在值。如果存在值,那么我们就将这个船的编号更新到第二个map中。
    • 直到读取完所有的数据,统计每个map的键中对应的值列表,返回第二个更新过了的map。

(….程序已经跑了一下午了…. 奶奶的,心疼我的电脑~~~~)
(六小时later~~~~)
(八小时~~~)
在这里插入图片描述
以我的经验来看,程序一直处于这个运行状态,应该是没有问题的。但是我感觉就目前的数据量,似乎~~不应该运行这么久(😂)
不想糟蹋我的电脑了~~扔到服务器上去跑吧!!!
(在服务器上又跑了一天,出现了connection reset by (server_ip_address) port 22 的程序中断~~)
这个原因,不知道是不是程序字符串的存储的原因~~
…..
字符串是一个对象,是在内存中存储的,没有长度限制,只有内存中堆内存满了,才会导致内存溢出,导致程序中断。所以这里我要将数据进行数据分批处理~~~

我打算将这个文件进行拆分进行处理!!
每一个文件夹下面大约100-150个文件~(这个数据量已经降低了很多了,应该没问题了)~当然这里也可以通过设置多个线程同时处理,为了避免不必要的错误。就直接分解文件了。
统计完后,会产生一个中间文件,用来显示每一个时刻,有着船舶记录的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
*
* @param dirpath 数据文件所在的文件夹目录
* @return
*/
public static Map<Integer, List<String>> FindDay(String dirpath){
//初始化一个map2 用来存储文件名
Map<Integer,List<String>> map2 = initTime();
//首先 获取文件列表(传入文件路径)
String [] filedir= getFilePath(dirpath);
//进行文件链路的拼接
List<String> nouse = new ArrayList<String>();
String head = dirpath;
List<String> filep = getpathList(head,filedir, nouse);
FileRead fRead = new FileRead();
for(int i=0;i<filep.size();i++) {
System.out.println(filep.get(i));
List<List<String>> file1 = fRead.readFILE2(filep.get(i));
Map<Integer, List<String>> map1 = setSingleShip(file1);
//进行数据的比较
Set<Integer> key = map2.keySet();

for (Integer k2 : key) {
List<String> value1 = map1.get(k2);
//根据m2的键去拿map1的值,如果m1不为空,则说明有位置数据
if(!value1.isEmpty()) {
//获得 map2中的值,并更新
List<String> value2 = map2.get(k2);
System.out.println(value2.isEmpty());
value2.add(value1.get(0));
}
}
}

return map2;
}

函数调用

1
2
3
4
5
System.out.println("test8开始");
Map<Integer, List<String>> file8 = FindDay("./test/202012Single2/test8/");
String value8 = WriteValue(file8);
writerFile("./test/test8.txt", value8);
System.out.println("test8结束");

test.txt的内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
24551988 13881.0 10986.0 13528.0 14208.0 13648.0 13940.0
24551991 13881.0 10986.0 13528.0 14208.0 13648.0 13940.0
24551994 13784.0 13881.0 10986.0 13528.0 14208.0 14339.0 13648.0 13940.0
24551997 13950.0 13881.0 10986.0 13528.0 14208.0 13648.0 13940.0
24552000 13950.0 13784.0 13881.0 10986.0 13528.0 14208.0 13648.0 13940.0
24552003 13950.0 11202.0 12681.0 13881.0 10986.0 13528.0 14208.0 13648.0 13940.0
24552006 13950.0 12681.0 13881.0 10986.0 13528.0 14208.0 13648.0 13940.0
24552009 13950.0 12681.0 13881.0 10986.0 13528.0 14208.0 13940.0
24552012 13950.0 12681.0 13881.0 10986.0 13528.0 14208.0 13940.0
24552015 13950.0 12681.0 14014.0 10986.0 13528.0 14208.0 13940.0
24552018 11341.0 13950.0 12681.0 14014.0 10986.0 13528.0 14208.0 9997.0 13940.0
24552021 13950.0 12681.0 14014.0 10986.0 13528.0 14208.0 13940.0
24552024 13950.0 12681.0 14014.0 10986.0 13528.0 14208.0 14339.0 13648.0 13940.0
24552027 13950.0 12681.0 14014.0 10986.0 13528.0 14208.0 13648.0 13940.0
24552030 13950.0 13784.0 12681.0 14014.0 10986.0 13528.0 14208.0 13648.0 13940.0
24552033 13950.0 11202.0 12681.0 14014.0 10986.0 13528.0 14208.0 13648.0

然后我们在对这些时刻进行统计,得到一个汇总表:
对于这部分我的思路是:

  • 首先初始化一个时间map<integer,integer>键为时间戳,值为这个时间下,有船在航行的记录数据
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 这个方法是为了统计使用 参数flag这是为了与第一个方法进行区分
* @param flag 方法重载 标志位 true或flase都可
* @return
*/
public static Map<Integer,Integer> initTime(boolean flag){
//开始时间是24542880 结束时间是24587520
Map<Integer,Integer> timeMap= new LinkedHashMap<Integer,Integer>();
for(int i=24542880;i<=24587520;i+=3) {
timeMap.put(i, 0);
}
return timeMap;
}
  • 读取文件夹列表,找到中间文件进行统计
  • 对文件进行读取,为了减少文件的IO操作,我选择将一个文件读取完之后,在从新进行统计,这里我们就要新增加一个函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public List<List<String>> readFILE3(String path) {
File file = new File(path);
List<List<String>> allList = new ArrayList<List<String>>();
BufferedReader reader = null;
try {
Reader inputStream = new FileReader(file);
reader = new BufferedReader(inputStream);
String line = null;
while((line = reader.readLine())!=null) {
String liString [] = line.split(" ");
List<String> list = new ArrayList<String>();
for(String s:liString) {
list.add(s);
}
allList.add(list);
}
return allList;

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
  • 统计找出主要的活动时间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/**
*
* @param dirpath 数据文件所在的文件夹目录
* @return
*/
public static Map<Integer, List<String>> FindDay(String dirpath){
//初始化一个map2 用来存储文件名
Map<Integer,List<String>> map2 = initTime();
//首先 获取文件列表(传入文件路径)
String [] filedir= getFilePath(dirpath);
//进行文件链路的拼接
List<String> nouse = new ArrayList<String>();
String head = dirpath;
List<String> filep = getpathList(head,filedir, nouse);
FileRead fRead = new FileRead();
for(int i=0;i<filep.size();i++) {
System.out.println(filep.get(i));
List<List<String>> file1 = fRead.readFILE2(filep.get(i));
Map<Integer, List<String>> map1 = setSingleShip(file1);
//进行数据的比较
Set<Integer> key = map2.keySet();

for (Integer k2 : key) {
List<String> value1 = map1.get(k2);
//根据m2的键去拿map1的值,如果m1不为空,则说明有位置数据
if(!value1.isEmpty()) {
//获得 map2中的值,并更新
List<String> value2 = map2.get(k2);
System.out.println(value2.isEmpty());
value2.add(value1.get(0));
}
}
}

return map2;
}

//对一个月以来所有船的活动时间进行一个统计找出,主要的活动时间
public static Map<Integer,Integer> getMainTime(String path) {
//首先要初始化一个时间
Map<Integer,Integer> timeMap = initTime(true);
//文件夹列表
//首先 获取文件列表(传入文件路径)
String [] filedir= getFilePath(path);
//进行文件路径的拼接
//进行文件链路的拼接
List<String> nouse = new ArrayList<String>();
String head = path;
List<String> filep = getpathList(head,filedir, nouse);
//文件读取
FileRead fRead = new FileRead();
for(int i=0;i<filep.size();i++) {
List<List<String>> fi = fRead.readFILE3(filep.get(i));
for(int j = 0;j<fi.size();j++){
String k = fi.get(j).get(0);
int value = fi.get(j).size()-1;
if(value!=0) {
System.out.println("va"+value);
}
//更新map的值
int mv =timeMap.get(Integer.parseInt(k));
timeMap.put(Integer.parseInt(k), value+mv);
}
}
return timeMap;
}
  • 将分钟统计到每一天并进行可视化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//返回一个字符串数组
public static String [] pytformat(String path) {
Map<Integer,Integer> timeMap = getMainTime(path);
String a = "[";
String b = "[";
String [] arr = new String[2];

int vee [] = new int[32];
//每三分钟执行一次,对60*24取余数就是为了确定天数
int h = 60*24;
int fist = 24542880;
Set<Integer> set = timeMap.keySet();
for(int k : set) {
vee [(k-fist)/h] = vee [(k-fist)/h]+timeMap.get(k);
}
for(int i=0;i<vee.length;i++) {
a+=(","+i);
b+=(","+vee[i]);
}

a+="]";
b+="]";
arr[0] =a;
arr[1] =b;
return arr;
}

下面是统计分析的结果,从这个结果可以看出,这一批船的主要活动轨迹时间是9月22号到30号之间。
在这里插入图片描述

统计每条船的22-30号活动轨迹的占比

筛选出我们真正要用的船,根据上面的统计,我们可以发现船的记录主要是集中在22-30号之间,接下来我们在对所有的船进行筛选,并统计每条船中22-30号之间的有船的记录,占该船所有记录中的比重。

  • 第一次将所有的船的比重做一个统计,可以发现,这批船的数据中,所有的船的占比的分布。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//文件统计单个船的数据是否所占比例较高  数据格式 10 10032.0 30.21381166666667 123.57897666666667
public double getSingleShip(String path) {
File file = new File(path);
BufferedReader reader = null;
//创建一个变量用来统计一共含有多少条内容
double total = 1;
//创建一个变量用来统计符合我们要统计的内容
double aim = 0;
try {
Reader inputStream = new FileReader(file);
reader = new BufferedReader(inputStream);
String line = null;

while((line = reader.readLine())!=null) {
System.out.println(line);
String liString [] = line.split(" ");
if(liString.length>=2) {
++total;
}
int curtime = Integer.parseInt(liString[0]);
int h = 60*24;
int fist = 24542880;
int lower = (curtime-fist)/h;
if(liString.length==4&&lower<=30&& lower>=22) {
++aim;
}
}
return aim/total;

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return aim/total;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//统计出19-30号 十天中,占整个月的比例较大的船,并根据比例尺 统计所占比例的分布
/**
*
* @param path 读取的文件路径
* @param jiange 船的占比的间隔
*/
public static String getUseShip(String path,int jiange){
//首先获取文件列表(传入文件路径)
//使用map进行存储 键是 文件名 值是 这条船19-30号的记录所占的比例
HashMap<String, Double> map = new HashMap<String, Double>();
HashMap<Integer, Integer> smap = new HashMap<Integer, Integer>();
String [] fieldir = getFilePath(path);
//进行文件路径的链路的拼接 这是一个集合 排除集合
List<String> nouse = new ArrayList<String>();
List<String> fielp = getpathList(path, fieldir, nouse);
for(int i =0;i<fielp.size();i++) {
String aaa = fielp.get(i);
FileRead fRead = new FileRead();
double aim = fRead.getSingleShip(aaa);
String key = aaa.substring(aaa.lastIndexOf("/"));
map.put(key, aim);
int a = (int)(aim*100/jiange);
if(smap.containsKey(a)) {
smap.put(a,smap.get(a)+1);
}else {
smap.put(a,1);
}
}
String value = WriteValue(map);
writerFile("./test/ttt.txt", value);
Set<Integer> keSet = smap.keySet();
for(int k : keSet) {
System.out.println(k+" "+smap.get(k));
}
return null;
}
  • 经过上述统计
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public static void deleFile(String path) {
    File file = new File(path+"/ttt.txt");
    BufferedReader reader = null;
    try {
    Reader inputStream = new FileReader(file);
    reader = new BufferedReader(inputStream);
    String line = null;
    while((line = reader.readLine())!=null) {
    String liString [] = line.split(" ");
    System.out.println(liString.length);
    if(Double.parseDouble(liString[1])<0.80) {
    File file2 = new File(path+"/updataData"+liString[0]);
    file2.delete();
    System.out.println("删除"+liString[0]);
    }
    }

    } catch (Exception e) {
    System.out.println(e);
    }

    }

我们这里统计了两部分数据,一部分是19-30号所占的比例,另一部分是22-30号所占的比例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 19-30
0 8
1 1
5 1
7 5
8 11
9 11
10 21
11 11
12 20
13 28
14 25
15 30
16 50
17 89
18 159
19 872

# 22 -30
0 3
4 1
8 1
11 2
12 1
13 5
14 41
15 302
16 339
17 96
18 36
19 45
  • 将筛选完的船只截取22 -30 号的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public List<List<String>> readFILE4(String path) {
File file = new File(path);
List<List<String>> allList = new ArrayList<List<String>>();
BufferedReader reader = null;
try {
Reader inputStream = new FileReader(file);
reader = new BufferedReader(inputStream);
String line = null;
while((line = reader.readLine())!=null) {
System.out.println(line);
String liString [] = line.split(" ");
System.out.println(liString.length);
List<String> list = new ArrayList<String>();
int curtime = Integer.parseInt(liString[0]);
int h = 60*24;
int fist = 24542880;
int lower = (curtime-fist)/h;
if(lower<=30&& lower>=22) {
for(String s:liString) {
list.add(s);
}
}
allList.add(list);
}
return allList;

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//将比例占比比较大的船的22-30号这几天的数据进行转存
public static void subData(String path,String target) {
//首先 获取文件列表(传入文件路径)
String [] filedir= getFilePath(path);
//进行文件链路的拼接 这一个集合是排除的集合
List<String> nouse = new ArrayList<String>();
//String head = "";
List<String> filep = getpathList(path,filedir, nouse);
FileRead read = new FileRead();
for (int i=0;i<filep.size();i++) {
String aaa = filep.get(i);
List<List<String>> file1 = read.readFILE4(aaa);
String value = WriteValue(file1);
writerFile(target+aaa.substring(aaa.lastIndexOf("/")), value);
}
}

数据缺失值的处理

对于数据的缺失主要分为以下三种情况:
-中间缺失一条(取平均值)
-中间缺失5条以内(根据前面的两条数据的间隔,估计后面的x,y)
-中间缺失5条以上 (缺失五条以上的数据,也就是船在15分钟以上没有发射记录,或者发送了记录,但是发送的记录的条数非常的少,可以默认为这个时间的船舶是静止的)
在实际的数据中,数据的情况比较复杂,因此在程序编写的过程中,需要根据数据的具体情况,对程序进行进一步的调整。因此,这一部分的代码并不是通用的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
public static List<List<String>> insertSinData(List<List<String>> page) {
//船号
String shipn = " ";
for(int i =0;i<page.size();i++) {
if(page.get(i).size()==4) {
shipn = page.get(i).get(1);
break;
}
}
/**
* 文件刚开始就有数据的缺失,直接用遇到的第一条完整的数据进行补充
*/
if(page.get(0).size()==1) {
for(int i =0;i<page.size()-1;i++) {
List<String> curline = page.get(i);
if(curline.size() ==4) {
for(int j=i-1;j>=0;j--) {
String x = curline.get(2);
String y = curline.get(3);
List<String> line = page.get(j);
line.add(shipn);
line.add(x);
line.add(y);
}
break;
}
}
}

for(int i =1;i<page.size()-1;i++) {
List<String> lineList = page.get(i);
if(lineList.size()==1) {

//中间缺失一条数据 x 与 y就直接取平均值
List<String> pro = page.get(i-1);
List<String> last = page.get(i+1);
//步骤一
if(pro.size()==4&&last.size()==4) {
//先将船号添加进去
lineList.add(shipn);
String curx = String.format("%.2f",((Double.parseDouble(pro.get(2))+Double.parseDouble(last.get(2)))/2));
String cury = String.format("%.2f",(Double.parseDouble(pro.get(3))+Double.parseDouble(last.get(3)))/2);
lineList.add(curx);
lineList.add(cury);
}
//步骤二
else if(pro.size()==4&&last.size()!=4) {
List<String> lastline = null;
int flag = 0;
for(int z =i;z<page.size()-1;z++) {
List<String> curline = page.get(z);
if(curline.size()!=4) {
flag++;
if(z==page.size()-2) {
lastline = curline;
break;
}
}else {
lastline = curline;
System.out.println(curline);
break;
}
}
//步骤2.1
if(flag >5) {
//如果中间空缺的值多于5个 就默认为这15分钟,船没有移动,或者应为其他原因,
//船舶停止数据的传输,这段时间,默认为船舶是静止的(中间数据为平均值)
for(int z = i;z<i+flag;z++) {
List<String> curline = page.get(z);
curline.add(shipn);
String curx ="";
String cury ="";
if(pro.size()==4&& lastline.size()==4) {
curx = String.format("%.2f",(Double.parseDouble(pro.get(2))+(Double.parseDouble(lastline.get(2))))/2);
cury = String.format("%.2f",(Double.parseDouble(pro.get(3))+(Double.parseDouble(lastline.get(3))))/2);
}else {
curx = pro.get(2);
cury = pro.get(3);
}
curline.add(curx);
curline.add(cury);
}
}
//步骤2.2
else {
for(int z = i;z<i+flag;z++) {
List<String> curline = page.get(z);
curline.add(shipn);
List<String> protwo = null;
List<String> proOne = null;
if(i==1) {
proOne = page.get(z-1);
protwo = proOne;
}else {
protwo = page.get(z-2);
proOne = page.get(z-1);
}
System.out.println(proOne);
System.out.println(protwo);
double x = Double.parseDouble(proOne.get(2))+(Double.parseDouble(proOne.get(2))-Double.parseDouble(protwo.get(2)));
double y = Double.parseDouble(proOne.get(3))+(Double.parseDouble(proOne.get(3))-Double.parseDouble(protwo.get(3)));
if(Math.abs(x)<=10&&Math.abs(y)<=10) {
String curx = String.format("%.2f",(Double.parseDouble(pro.get(2))+(Double.parseDouble(lastline.get(2))))/2);
String cury = String.format("%.2f",(Double.parseDouble(pro.get(3))+(Double.parseDouble(lastline.get(3))))/2);
curline.add(curx);
curline.add(cury);
}else {
String curx = String.format("%.2f",x);
String cury = String.format("%.2f",y);
curline.add(curx);
curline.add(cury);
}
}
}
}
}
}
return page;
}

控制程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//控制总的插值
public static void insertData(String path,String target) {
//首先 获取文件列表(传入文件路径)
String [] filedir= getFilePath(path);
//进行文件链路的拼接 这一个集合是排除的集合
List<String> nouse = new ArrayList<String>();
//String head = "";
List<String> filep = getpathList(path,filedir, nouse);
FileRead read = new FileRead();
for (int i=0;i<filep.size();i++) {
String aaa = filep.get(i);
List<List<String>> file1 = read.readFILE3(aaa);
List<List<String>> upfile1 = insertSinData(file1);
String value = WriteValue(upfile1);
writerFile(target+aaa.substring(aaa.lastIndexOf("/")), value);
}
}

插值方法需要改进

中间缺失五条的数据进行线性插值

原理

已经知道点(x0,y0),(x1,y1)然后根据在这里插入图片描述
进行计算。然后修改 代码!
因为我们要修改X,Y的值,这两个值的缺失是同时的。因此,我们就设置时间戳为我们线性插值,所用的X,使用数据中缺失的X,Y来作为线性插值的Y。

对数据根据时间进行排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 对数据根据时间进行排序(需要维护一个非常大的轨迹文件)
时间复杂度比较低,但是I/O操作比较多,对文件需要不停的进行读取操作 然后在拼接到一个文件中
* @param path 源文件夹的路径 后面需要添加 /
* @param target 目标文件的完整路径,需要具体到一个文件
*/
public static void sortFile(String path,String target) {
//获取所有要读取的文件列表
//首先 获取文件列表(传入文件路径)
String [] filedir= getFilePath(path);
//进行文件链路的拼接 这一个集合是排除的集合
List<String> nouse = new ArrayList<String>();
//String head = "";
List<String> filep = getpathList(path,filedir, nouse);
FileRead read = new FileRead();
List<List<String>> firstpage = read.readFILE3(filep.get(0));
//查看文件中一共有几条记录
for(int i=0;i<firstpage.size()-1;i++) {
//一次写入一个时刻的值
//文件路径
List<LinkedList<String>> onetime = new LinkedList<LinkedList<String>>();
for(int j = 0;j<filep.size();j++) {
String aaa = filep.get(j);
List<List<String>> page = read.readFILE3(aaa);
onetime.add((LinkedList<String>)page.get(i));
}
String value = WriteValue2(onetime);
writerFile(target, value);
}
}

将文件中所需要的数据统计出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//读取文件并且进行统计操作 不需要返回值 直接输出就好
public void readFILE5(String path) {
File file = new File(path);
BufferedReader reader = null;
double mintime = Double.MAX_VALUE;
double maxtime =Double.MIN_VALUE;
double minx = Double.MAX_VALUE;
double maxx = Double.MIN_VALUE;
double miny = Double.MAX_VALUE;
double maxy =Double.MIN_VALUE;
try {
Reader inputStream = new FileReader(file);
reader = new BufferedReader(inputStream);
String line = null;
while((line = reader.readLine())!=null) {
System.out.println(line);
String liString [] = line.split(" ");
if(liString.length == 4) {
double time = Double.parseDouble(liString[0]);
double x = Double.parseDouble(liString[2]);
double y = Double.parseDouble(liString[3]);
if(time>maxtime) {
maxtime = time;
}
if(time<mintime) {
mintime=time;
}

if(x<minx) {
minx = x;
}
if(x>maxx) {
maxx = x;
}

if(y<miny) {
miny = y;
}
if(y>maxy) {
maxy = y;
}
}
}
System.out.println("mintime:"+String.format("%.2f",mintime)+"maxtime"+String.format("%.2f",maxtime)+"minx"+String.format("%.2f",minx)+"maxx"+String.format("%.2f",maxx)+"miny"+String.format("%.2f",miny)+"maxy"+String.format("%.2f",maxy));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

控制函数

1
2
3
4
5
//统计 最小时间  最大时间  最小X  最大X 最小Y 最大Y
public static void getFileHead(String path) {
FileRead read = new FileRead();
read.readFILE5(path);
}

船舶的轨迹数据的记录使用的单位是公里

1节 = 1海里/小时 = 1.852公里/小时

统计每条船的相遇情况

什么叫相遇?
两条船在同一时刻,在对方的通信范围内。
船的通信范围需要设置多大?25海里
需要记录什么?

船的id [ 相遇的船id: 次数-相遇间隔 , ……..]
如何统计?
循环遍历每一条船,比较两条船在相应的时刻是否在我们规定的2倍的通信范围内。
如果在这一个时刻在通信范围内,则记录一次相遇,然后判断下一个时刻船是否依旧在通信范围内,如果在通信范围内则不进行累加,在这段时间属于伴随关系。如果不在通信范围内,则跳过。继续循环上述过程。
代码该如何实现?
先取出一条船A的数据,然后迭代遍历剩余的所有的船,拿出一条船B。
使用船A的每一个时刻的数据,与船B对应时刻的数据进行比较,看一下两个时刻船的相对距离,是否在通信范围内。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// 统计两条船的相遇次数 以及间隔
public static List<String> getMeetCount(List<List<String>> first,List<List<String>> second) {

//定义一个存储上一个时刻的变量
double pretime = 0.0;
//定义一个变量用来 存储船的相遇次数
int count = 0;
String secshipid = second.get(0).get(1);
String firshipid = first.get(0).get(1);
System.out.println("船"+firshipid+"相遇"+secshipid+"getMeetCount() start");
List<String> lStrings = new LinkedList<String>();
lStrings.add(firshipid);
lStrings.add(secshipid);
//初始化一个用来记录两条船时间间隔的链表
List<String> banshui = new LinkedList<String>();
//定义一个变量用来存储在船相遇的时候,伴随了几个间隔
int b = 0;
//定义一个旗帜来表示这段间隔是不是船开始伴随了
boolean flag = false;
System.out.println(first.size());
System.out.println(second.size());
if(first.size()==second.size()) {
for(int i=0;i<first.size()-1;i++) {

//获得第一条船的第i时刻的记录
List<String> fistline = first.get(i);
//获得第二条船的第i时刻的记录
List<String> secondline = second.get(i);
//记录一下当前时刻
double curtime = Double.parseDouble(fistline.get(0));
//获得第一条船的第i时刻的 x y
double firstx = Double.parseDouble(fistline.get(2));
double firsty = Double.parseDouble(fistline.get(3));
//获得第二条船的第i时刻的x y
double secondx = Double.parseDouble(secondline.get(2));
double secondy = Double.parseDouble(secondline.get(3));
//计算一下 两个点之间的距离 与 船舶的二倍的通信呢半径比较一下
double x = Math.pow((secondx-firstx), 2);
double y = Math.pow((secondy-firsty), 2);
double distance = Math.sqrt(x+y);
//设置一下船的通信范围
double contactRange = 1*1.87;
// System.out.println("pre"+pretime);
// System.out.println("cur"+curtime);
// System.out.println(distance);
if(distance<=contactRange*2 ) {
if(curtime-pretime>3) {
pretime = curtime;
count++;
flag= true;
b++;
}else {
pretime = curtime;
flag = true;
b++;
//System.out.println("b"+b);
}
}else {
flag=false;
}
if(!flag && b!=0) {
// System.out.println(flag);
banshui.add(String.valueOf(b*3));
b=0;
}
}
}
if(b!=0) {
//System.out.println("out"+b);
banshui.add(String.valueOf(b*3));
}
lStrings.add(String.valueOf(count));
lStrings.add(String.valueOf(banshui));
System.out.println("船"+firshipid+"相遇"+secshipid+"次数为:"+count);
System.out.println("getMeetCount() end");
return lStrings;
}
//统计船的相遇次数的控制程序
/**
*
* @param path 后面需要加/
*/
public static void getMeetCounContral(String path,String target,String shippath) {
//初始化一个list 用来存储船舶之间的对应关系
//List<List<String>> ls = new LinkedList<List<String>>();

//一个list用来存储所有的船的编号
String totalshipid = "";
//获取所有要读取的文件列表
//首先 获取文件列表(传入文件路径)
String [] filedir= getFilePath(path);
//进行文件链路的拼接 这一个集合是排除的集合
List<String> nouse = new ArrayList<String>();
//String head = "";
List<String> filep = getpathList(path,filedir, nouse);
FileRead read = new FileRead();
for(int i=0;i<filep.size();i++) {
List<List<String>> firstpage = read.readFILE3(filep.get(i));
totalshipid=" "+firstpage.get(0).get(1);
//写入船的号码
writerFile(shippath, totalshipid);
for(int j=i;j<filep.size();j++) {
if(i!=j) {
List<List<String>> secondpage = read.readFILE3(filep.get(j));
List<String> lineList= getMeetCount(firstpage, secondpage);
//写入文件
String value = WriteValue3(lineList);
writerFile(target, value);
}
}

}

}

最后是将统计的结果进行图形化展示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from pyecharts import options as opts
from pyecharts.charts import Graph
rela = open("/Users/gorge/Desktop/newshipData/out/relation.txt");
shipid = open("/Users/gorge/Desktop/newshipData/out/shipid.txt")
nodes_data = []
links_data = []
for line in rela.readlines():
dataline = line.strip('\n').split("[")[0].split(" ")
if str(dataline[2])!='0':
shijian = line.strip('\n').split("[")[1][:-1].split(",")
#先算一个总的时间T
T=0
for a in shijian:
if a!='':
T=T+(int(a.strip(" ")))
famil=0
for x in shijian:
if x!='':
yy = int(a.strip(" "))
famil=(famil+(yy**2/T))
print("famil",famil)
links_data.append(
opts.GraphLink(source=str(dataline[0]), target=str(dataline[1]), value=round(famil,2)
))

dataline2 = shipid.readlines()[0].split(" ")
for id_q in dataline2:
nodes_data.append(opts.GraphNode(name=str(id_q), symbol_size=10))
c = (
Graph(init_opts=opts.InitOpts(width="1500px", height="800px"))
.add(
"",
nodes_data,
links_data,
repulsion=10,
edge_label=opts.LabelOpts(
is_show=True, position="middle", formatter="{c}"
),
)
.set_global_opts(
title_opts=opts.TitleOpts(title="ship relation")
)
.render("/Users/gorge/Desktop/aaaaa.html")
)
print("over")

切记:不要创建一个非常大的字符串,然后一次性的写入文件。可能会由于内存不足,导致文件的写入不成功。或者程序执行非常缓慢。

熟悉度的计算进程

2020年11月30 日 相遇半径使用5海里
在这里插入图片描述